package rmi;

import java.io.Serializable;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class UtilitairesRmi implements Remote, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public static final String NOM_CONTROLLEUR_LOBBY = "LobbyControlleur";
	public static final String NOM_CONTROLLEUR_PARTIE = "PartieControlleur";

	private static final int NUMERO_PORT = 1337;

	Registry rmiRegistry = null;

	private static UtilitairesRmi instance;

	public static UtilitairesRmi getInstance() throws RemoteException, NotBoundException, AlreadyBoundException {
		if (instance == null) {
			synchronized(UtilitairesRmi.class) {
				instance = obtenirUtilitairesRmi();
				if (instance == null) {
					instance = new UtilitairesRmi();
				}
			}
		}
		return instance;
	}
	
	private static UtilitairesRmi obtenirUtilitairesRmi () {
		UtilitairesRmi instanceUtilitairesRmi = null;
		try {
			instanceUtilitairesRmi = (UtilitairesRmi)LocateRegistry.getRegistry(NUMERO_PORT).lookup("UtilitairesRmi");
		} catch (RemoteException e) {
			System.out.println("Le registry au port " + NUMERO_PORT + " n'est pas créé, il sera donc créé");
		} catch (NotBoundException e) {
			System.out.println("Le registry au port " + NUMERO_PORT + " n'est pas créé, il sera donc créé");
		}
		
		return instanceUtilitairesRmi;
	}



	private UtilitairesRmi() throws RemoteException, AlreadyBoundException {
		rmiRegistry = LocateRegistry.createRegistry(NUMERO_PORT);
		rmiRegistry.bind("UtilitairesRmi", (Remote) this);
		System.out.println("RmiRegistry created");
	}

	//Observable
	public void bind(RemoteObservable observable) throws AccessException, RemoteException, AlreadyBoundException {
		if (!verifierSiExisteDansRmi(observable.getNomRmi())) {
			this.rmiRegistry.bind(observable.getNomRmi(), UnicastRemoteObject.exportObject(observable, 0));
		}
	}

	public Object obtenirEtBind(RemoteObservable observable, boolean rebind) throws RemoteException, NotBoundException, AlreadyBoundException {
		boolean existe = verifierSiExisteDansRmi(observable.getNomRmi());

		if (existe && rebind) {
			this.rmiRegistry.rebind(observable.getNomRmi(), UnicastRemoteObject.exportObject(observable, 0));
		}
		else {
			this.rmiRegistry.bind(observable.getNomRmi(), UnicastRemoteObject.exportObject(observable, 0));
		}

		return this.rmiRegistry.lookup(observable.getNomRmi());
	}

	public Object obtenirObjet(RemoteObservable observable) throws RemoteException, NotBoundException {
		if (verifierSiExisteDansRmi(observable.getNomRmi())) {
			return this.rmiRegistry.lookup(observable.getNomRmi());
		}

		return null;
	}

	public void unbind(RemoteObservable observable) throws RemoteException, NotBoundException {
		if (verifierSiExisteDansRmi(observable.getNomRmi())) {
			this.rmiRegistry.unbind(observable.getNomRmi());
		}
	}

	//Observer
	public void bind(RemoteObserver observer) throws AccessException, RemoteException, AlreadyBoundException {
		if (!verifierSiExisteDansRmi(observer.getNomRmi())) {
			this.rmiRegistry.bind(observer.getNomRmi(), UnicastRemoteObject.exportObject(observer, 0));
		}
	}

	public Object obtenirEtBind(RemoteObserver observer, boolean rebind) throws RemoteException, NotBoundException, AlreadyBoundException {
		boolean existe = verifierSiExisteDansRmi(observer.getNomRmi());

		if (existe && rebind) {
			this.rmiRegistry.rebind(observer.getNomRmi(), observer);
		}
		else if (!existe) {
			this.rmiRegistry.bind(observer.getNomRmi(), UnicastRemoteObject.exportObject(observer, 0));
		}

		return this.rmiRegistry.lookup(observer.getNomRmi());
	}

	public Object obtenirObjet(RemoteObserver observer) throws RemoteException, NotBoundException {
		if (verifierSiExisteDansRmi(observer.getNomRmi())) {
			return this.rmiRegistry.lookup(observer.getNomRmi());
		}

		return null;
	}

	public void unbind(RemoteObserver observer) throws RemoteException, NotBoundException {
		if (verifierSiExisteDansRmi(observer.getNomRmi())) {
			this.rmiRegistry.unbind(observer.getNomRmi());
		}
	}

	//Objet global
	public void bind(String nom, Object object) throws AccessException, RemoteException, AlreadyBoundException {
		if (!verifierSiExisteDansRmi(nom)) {
			this.rmiRegistry.bind(nom, UnicastRemoteObject.exportObject((Remote) object, 0));
		}
	}

	public Object obtenirObjet(String nom) {
		if (verifierSiExisteDansRmi(nom)) {
			try {
				return this.rmiRegistry.lookup(nom);
			} catch (RemoteException | NotBoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}

	public Object obtenirEtBind(String nom, Object object) throws AccessException, RemoteException, AlreadyBoundException, NotBoundException {
		this.bind(nom, object);

		return this.rmiRegistry.lookup(nom);
	}


	private boolean verifierSiExisteDansRmi(String nomObjet) {
		try {
			for (String objectRmi : this.rmiRegistry.list()) {
				if (objectRmi.equals(nomObjet)) {
					return true;
				}
			}
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return false;
	}

}
